package com.jl.registermvc;

import com.jl.annotation.JMvc;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.springframework.http.MediaType;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo.BuilderConfiguration;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Set;

/**
 * 这个是第三步 进行绑定url
 */
@Slf4j
public class ContractAutoHandlerRegisterHandlerMapping extends RequestMappingHandlerMapping {

    private String basePackage;

    private boolean isInterfaceNameSmall;

    private Reflections reflections;

    private BuilderConfiguration mappingInfoBuilderConfig;

    public ContractAutoHandlerRegisterHandlerMapping(String basePackage, boolean isInterfaceNameSmall) {
        super();
        this.basePackage = basePackage;
        this.isInterfaceNameSmall = isInterfaceNameSmall;
        this.reflections = new Reflections(basePackage);
        // 这里为了能够比SimpleMappingHandlerMapping早点使用，不然会匹配/** 的url
        super.setOrder(super.getOrder() - 1);
    }

    /**
     * 判断是否符合触发自定义注解的实现类方法
     */
    @SneakyThrows
    @Override
    protected boolean isHandler(Class<?> beanType) {
        // 过滤接口
        if (beanType.isInterface()) {
            return false;
        }
        // 过滤不实现接口的类
        Class<?>[] interfaces = beanType.getInterfaces();
        if (interfaces.length == 0) {
            return false;
        }
        // 过滤启动类
        String name = beanType.getName();
        name = name.indexOf("$$") != -1 ? name.substring(0, name.indexOf("$$")) : name;
        boolean annotationPresent = Class.forName(name).isAnnotationPresent(JMvc.class);
        if (annotationPresent) {
            return false;
        }
        // 是否在启动类包范围内
        return isPackageInScope(beanType);
    }

    /**
     * 扫到实现类的方法时，与这里返回的url进行板顶
     *
     * @param method      实现类方法
     * @param handlerType 实现类对象
     * @return
     */
    @Override
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        try {
            Class<?> contractMarkClass = null;
            Method interfaceSmethod = null;
            //遍历寻找方法所属的接口
            Class<?>[] interfaces = handlerType.getInterfaces();
            for (Class<?> anInterface : interfaces) {
                //接口不在启动类包范围内直接过滤
                boolean packageInScope = isPackageInScope(anInterface);
                if (!packageInScope) {
                    continue;
                }
                try {
                    interfaceSmethod = anInterface.getMethod(method.getName(), method.getParameterTypes());
                    contractMarkClass = anInterface;
                } catch (Exception e) {

                }
            }
            //判断是否加了@springmvc路由注解
            if (method.isAnnotationPresent(GetMapping.class)
                    || method.isAnnotationPresent(PostMapping.class)
                    || method.isAnnotationPresent(PutMapping.class)
                    || method.isAnnotationPresent(DeleteMapping.class)
                    || method.isAnnotationPresent(PatchMapping.class)
                    || method.isAnnotationPresent(RequestMapping.class)
                    || interfaceSmethod.isAnnotationPresent(GetMapping.class)
                    || interfaceSmethod.isAnnotationPresent(PostMapping.class)
                    || interfaceSmethod.isAnnotationPresent(PutMapping.class)
                    || interfaceSmethod.isAnnotationPresent(DeleteMapping.class)
                    || interfaceSmethod.isAnnotationPresent(PatchMapping.class)
                    || interfaceSmethod.isAnnotationPresent(RequestMapping.class)) {
                return null;
            }
            //封装请求
            RequestMappingInfo info = buildRequestMappingByMethod(method);
            if (info != null) {
                //如果接口被多个类实现，则路由变为实现类名
                Set<Class<?>> subTypesOf = reflections.getSubTypesOf((Class<Object>) contractMarkClass);
                RequestMappingInfo typeInfo = buildRequestMappingByClass(subTypesOf.size() > 1 ? handlerType : contractMarkClass);
                if (typeInfo != null) {
                    info = typeInfo.combine(info);
                }
            }
            return info;
        } catch (Exception ex) {
            return null;
        }
    }

    private RequestMappingInfo buildRequestMappingByClass(Class<?> contractMarkClass) {
        //原接口名
        String simpleName = contractMarkClass.getSimpleName();
        //接口名首字母转小写
        if (isInterfaceNameSmall) {
            simpleName = (new StringBuilder()).append(Character.toLowerCase(simpleName.charAt(0))).append(simpleName.substring(1)).toString();
        }
        String[] paths = new String[]{simpleName};
        RequestMappingInfo.Builder builder = RequestMappingInfo.paths(resolveEmbeddedValuesInPatterns(paths));
        // 通过反射获得 config
        BuilderConfiguration config = getConfig();
        this.mappingInfoBuilderConfig = config;
        if (this.mappingInfoBuilderConfig != null) {
            return builder.options(this.mappingInfoBuilderConfig).build();
        } else {
            return builder.build();
        }
    }

    private RequestMappingInfo buildRequestMappingByMethod(Method originalMethod) {
        // 用名字作为url
        // 有@RequestBody post形式,application/json请求 否则get形式,*/*请求
        String[] paths = new String[]{originalMethod.getName()};
        String consumes = MediaType.ALL_VALUE;
        RequestMethod methodType = RequestMethod.GET;
        Parameter[] parameters = originalMethod.getParameters();
        if (parameters.length > 0) {
            for (Parameter parameter : parameters) {
                RequestBody annotation = parameter.getAnnotation(RequestBody.class);
                if (annotation != null) {
                    consumes = MediaType.APPLICATION_JSON_VALUE;
                    methodType = RequestMethod.POST;
                    break;
                }
            }
        }
        RequestMappingInfo.Builder builder = RequestMappingInfo.paths(resolveEmbeddedValuesInPatterns(paths))
                .consumes(consumes)
//                .params(requestMapping.params())
//                .mappingName(name)
//                .headers("Content-Type=" + MediaType.APPLICATION_JSON_VALUE)
                .produces(MediaType.APPLICATION_JSON_VALUE)
                .methods(methodType);
        return builder.options(this.getConfig()).build();
    }

    private BuilderConfiguration getConfig() {
        Field field;
        BuilderConfiguration configChild = null;
        try {
            field = RequestMappingHandlerMapping.class.getDeclaredField("config");
            field.setAccessible(true);
            configChild = (BuilderConfiguration) field.get(this);
        } catch (IllegalArgumentException | IllegalAccessException e) {
            log.error(e.getMessage(), e);
        } catch (NoSuchFieldException | SecurityException e) {
            log.error(e.getMessage(), e);
        }
        return configChild;
    }

    /**
     * 判断指定类是否在包范围内
     *
     * @param beanType 指定类
     * @return 如果在范围内返回 true，否则返回 false
     */
    private boolean isPackageInScope(Class<?> beanType) {
        // 是否在包路径内
        String packageName = ClassUtils.getPackageName(beanType);
        if (packageName.startsWith(basePackage)) {
            return true;
        }
        return false;
    }

}
